﻿using Microsoft.Extensions.Caching.Memory;
using RailChess.GraphDefinition;

namespace RailChess.Play.Services.Core
{
    public class CoreGraphProvider
    {
        private readonly PlayEventsService _eventsService;
        private readonly PlayToposService _topoService;
        private readonly CoreGraphConverter _converter;
        private readonly CoreGraphEvaluator _evaluator;
        private readonly IMemoryCache _cache;
        private readonly ILogger<CoreGraphProvider> _logger;

        public CoreGraphProvider(
            PlayEventsService eventsService, PlayToposService toposService,
            CoreGraphConverter converter, CoreGraphEvaluator evaluator,
            IMemoryCache cache, ILogger<CoreGraphProvider> logger) 
        {
            _eventsService = eventsService;
            _topoService = toposService;
            _converter = converter;
            _evaluator = evaluator;
            _cache = cache;
            _logger = logger;
        }

        private Graph? GraphThisScope { get; set; } //非static，每个scope独立，不会有线程不安全问题
        public Graph GetGraph()
        {
            var latestOpId = _eventsService.LatestOperation()?.Id ?? 0;
            //判断当前结果是否过期
            if (GraphThisScope is { } && GraphThisScope.OpEventId == latestOpId)
            {
                //未过期（eventId相同）可直接使用
                //LatestOperation未变，则玩家位置、站点拥有者必然完全相同，无需重新计算
                _logger.LogDebug("游戏[{gameId}]_直接使用同Scope已构建核心图", _topoService.GameId);
                return GraphThisScope;
            }
            _logger.LogDebug("游戏[{gameId}]_构建核心图", _topoService.GameId);
            var topo = _topoService.OurTopo();
            var graph = _converter.Convert(topo) ?? throw new Exception("地图数据异常(无法构建图)");
            graph.OpEventId = latestOpId; //设置当前的lastestOpId
            GraphThisScope = graph; //保存下来（仅本scope存在，非static）
            var ocps = _eventsService.PlayerCaptureEvents().ConvertAll(x =>new { x.PlayerId, x.StationId });
            ocps.ForEach(o =>
            {
                var sta = graph.Stations.Find(s => s.Id == o.StationId);
                if (sta is not null)
                    sta.Owner = o.PlayerId;
            });
            var locEvents = _eventsService.PlayerLocateEvents();
            var outPlayerIds = _eventsService.PlayerOutEvents().ConvertAll(x=>x.PlayerId);
            locEvents.ForEach(x =>
            {
                if (!outPlayerIds.Contains(x.PlayerId))
                    graph.UserPosition.Add(x.PlayerId, x.StationId);
            });
            return graph;
        }

        public Dictionary<int,int> StationDirections()
        {
            var graph = GetGraph();
            return _evaluator.StationDirections(graph);
        }
        public int TotalDirections(List<int> staIds)
        {
            var dirDict = StationDirections();
            return TotalDirections(staIds, dirDict);
        }
        public int TotalDirections(List<int> staIds, Dictionary<int,int> dirDict)
        {
            int sum = 0;
            staIds.ForEach(x =>
            {
                if (dirDict.TryGetValue(x, out int value))
                {
                    sum += value;
                }
            });
            return sum;
        }
        public List<int> PureTerminals()
        {
            var dirDict = StationDirections();
            var res = dirDict.Where(x=>x.Value==1).Select(x=>x.Key).ToList();
            return res;
        }
        public List<int> TwinExchanges()
        {
            var graph = GetGraph();
            var res = graph.Stations.Where(x =>
            {
                var n = x.Neighbors;
                if (n.Count == 4)
                {
                    //其实并不是“双线换乘站”，是“十字换乘站”，也可能是三条或者四条线
                    if (n.Select(x => x.Station.Id).Distinct().Count() == 4)
                        return true;
                }
                return false;
            }).Select(x => x.Id).ToList();
            return res;
        }
    }
}
